/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.utils
{
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	import org.etherframework.core.utils.utilsClasses.ListenerInfo;

	public class Dispatcher
	{
		private const _dictionary:Dictionary = new Dictionary();
		
		private var _source:IEventDispatcher;
		private var _interceptor:Function;

		public function Dispatcher(source:IEventDispatcher = null, 
								   interceptor:Function = null)
		{
			_source = source;
			_interceptor = interceptor;
		}
		
		public function listen(owner:Object, event:String, listener:Function, 
							   useCapture:Boolean = false, priority:int = 0, 
							   useWeakReference:Boolean = false):ListenerInfo
		{
			if (!(owner && event && (listener is Function)))
				throw new ArgumentError("Arguments owner:Object, event:String or " +
					"listener:Function can not be null.");
			
			const list:Vector.<ListenerInfo> = _dictionary[owner] || 
				(_dictionary[owner] = new Vector.<ListenerInfo>());
			const info:ListenerInfo = getListenerInfo(event, listener, useCapture, priority);
			
			_source.addEventListener(info.type, info.handler, useCapture, priority, useWeakReference);
			list.push(info);
			
			return info;
		}
		
		public function unlisten(owner:Object, event:String, listener:Function, 
								 useCapture:Boolean = false):ListenerInfo
		{
			if (!(owner && event && (listener is Function)))
				throw new ArgumentError("Arguments owner:Object, event:String and " +
					"listener:Function can not be null.");
			
			const list:Vector.<ListenerInfo> = _dictionary[owner];
			
			for each (var item:ListenerInfo in list)
			{
				if (item.listener == listener && item.type == event && 
					item.useCapture == useCapture)
				{
					list.splice(list.indexOf(item), 1);
					_source.removeEventListener(item.type, item.handler, useCapture);
					break;
				}
			}
			
			if (!list.length)
				delete _dictionary[owner];
			
			return item;
		}
		
		public function unlistenAll(owner:Object):Vector.<ListenerInfo>
		{
			if (!owner)
				throw new ArgumentError("Argument owner:Object can not be null.");
			
			const list:Vector.<ListenerInfo> = _dictionary[owner];
			const source:IEventDispatcher = _source;
			for each (var item:ListenerInfo in list)
			{
				source.removeEventListener(item.type, item.handler, item.useCapture);
			}
			delete _dictionary[owner];
			
			return list;
		}
		
		public function listenInfo(owner:Object, items:Vector.<ListenerInfo>, 
								   avoidDuplicates:Boolean = true):void
		{
			if (!(owner && items))
				throw new ArgumentError("Argument owner:Object or " +
					"items:Vector.<ListenerInfo> can not be null.");
			
			const source:IEventDispatcher = _source;
			const list:Vector.<ListenerInfo> = _dictionary[owner] || 
				(_dictionary[owner] = new Vector.<ListenerInfo>());
			var info:ListenerInfo;
			var index:int;
			
			for each (var item:ListenerInfo in items)
			{
				if (avoidDuplicates)
				{
					source.removeEventListener(item.type, item.handler, item.useCapture);
					if ((index = list.indexOf(item)) != -1)
						list.splice(index, 1);
				}
				
				source.addEventListener(item.type, item.handler, item.useCapture, item.priority);
				list.push(item);
			}
		}
		
		public function unlistenInfo(owner:Object, items:Vector.<ListenerInfo>):void
		{
			if (!(owner && items))
				throw new ArgumentError("Argument owner:Object or " +
					"items:Vector.<ListenerInfo> can not be null.");
			
			const source:IEventDispatcher = _source;
			for each (var item:ListenerInfo in items)
			{
				source.removeEventListener(item.type, item.handler, item.useCapture);
			}
		}
		
		public function switchSource(source:IEventDispatcher) : void
		{
			if (!source)
				throw new ArgumentError("Argument source:IEventDispatcher can not be null.");
			
			if (_source != source)
			{
				var item:ListenerInfo;
				const oldSource:IEventDispatcher = _source;
				const dict:Dictionary = _dictionary;
				for each (var list:Vector.<ListenerInfo> in dict)
				{
					for each (item in list)
					{
						oldSource.removeEventListener(item.type, item.handler, item.useCapture);
						source.addEventListener(item.type, item.handler, item.useCapture, item.priority);
					}
				}
				_source = source;
			}
		}
		
		private function getListenerInfo(type:String, listener:Function, 
										 useCapture:Boolean, priority:int):ListenerInfo
		{
			var interceptor:Function;
			
			if (_interceptor is Function)
			{
				interceptor = function(event:Event):void 
				{
					var evt:Event = _interceptor(event);
					if (evt) { listener(evt); }
				};
			}
			
			return new ListenerInfo(type, listener, interceptor, useCapture, priority);
		}
		
		public function get interceptor():Function { return _interceptor; }
		public function set interceptor(value:Function):void 
		{
			if (_interceptor is Function)
				throw new IllegalOperationError("Interceptor is already set.");
			
			_interceptor = value; 
		}
		
		public function get source():IEventDispatcher { return _source; }
	}
}